package rpc

import (
	"context"
	"fmt"
	"google.golang.org/grpc"
	"google.golang.org/grpc/balancer/roundrobin"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/keepalive"
	"google.golang.org/grpc/metadata"
	"grpc.getaway.adminspro.padmins.com/pkg/config"
	"grpc.getaway.adminspro.padmins.com/pkg/libs/utils"
	"grpc.getaway.adminspro.padmins.com/pkg/support/log"
	"reflect"
	"sync"
	"time"
)

var (
	clientConns   = make(map[string]*grpc.ClientConn)
	clientConnsMu sync.RWMutex
)

func GetClientConn(name string) *grpc.ClientConn {
	clientConnsMu.RLock()
	clientConn, ok := clientConns[name]
	clientConnsMu.RUnlock()
	if ok {
		return clientConn
	}

	clientConnsMu.Lock()

	addressValue := reflect.ValueOf(config.Config.Rpc.Address).FieldByName(name)
	address := addressValue.String()
	if address == "<invalid Value>" {
		clientConnsMu.Unlock()
		panic(name + "RpcConfig can not fund")
	}

	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(5)*time.Second)
	defer cancel()

	opts := []grpc.DialOption{
		grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, roundrobin.Name)), // 负载均衡策略
		grpc.WithTransportCredentials(insecure.NewCredentials()),
		grpc.WithUnaryInterceptor(interceptor), // 拦截器
		grpc.WithKeepaliveParams(keepalive.ClientParameters{
			Time:                10 * time.Second, // 如果没有 activity， 则每隔此值发送一个 ping 包
			Timeout:             time.Second,      // 如果 ping ack 该值之内未返回则认为连接已断开
			PermitWithoutStream: true,             // 如果没有 active 的 stream， 是否允许发送 ping
		}),
	}
	clientConn, err := grpc.DialContext(ctx, address, opts...)

	if err != nil {
		clientConnsMu.Unlock()
	}
	clientConns[name] = clientConn
	clientConnsMu.Unlock()
	return clientConns[name]
}

func interceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
	start := time.Now()
	logger := log.WithContext(ctx)
	// 获取API的TraceId
	TraceId, _ := utils.ExtraTraceId(ctx)

	// 将TraceId写入 grpc的header中
	ctx = metadata.AppendToOutgoingContext(ctx, "trace-id", string(TraceId))

	var header metadata.MD
	opts = append(opts, grpc.Header(&header))
	logger.Info("grpc请求参数", map[string]interface{}{
		"method": method,
		"req":    req,
	})
	// 发送grpc请求
	err := invoker(ctx, method, req, reply, cc, opts...)
	if err != nil {
		logger.Error("grpc请求响应", map[string]interface{}{
			"method":   method,
			"rep":      reply,
			"error":    err,
			"duration": fmt.Sprintf("%v", time.Since(start)),
		})
	} else {
		logger.Info("grpc请求响应", map[string]interface{}{
			"method":   method,
			"error":    err,
			"duration": fmt.Sprintf("%v", time.Since(start)),
		})
	}
	return err
}
